#ifndef BZS_DB_ENGINE_MYSQL_PERCENTAGEKEY_H
#define BZS_DB_ENGINE_MYSQL_PERCENTAGEKEY_H
/* =================================================================
 Copyright (C) 2012 2013 BizStation Corp All rights reserved.

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 02111-1307, USA.
 ================================================================= */

#include "mysqlInternal.h"

namespace bzs
{
namespace db
{
namespace engine
{
namespace mysql
{

/** return value of setKeyValueByPer function
 *
 */
#define KEY_COPY_SUCCESS 0
#define KEY_ALL_SGMENTS_SAME 1
#define KEY_NEED_SGMENT_COPY 2

class percentageKey
{
    uchar* m_first;
    uchar* m_last;
    uchar* m_current;
    const KEY& m_key;
    double m_period;
    int m_lastCompSize;
    int m_lastCompPos;
    unsigned short* m_varlenPtr;
    const KEY_PART_INFO* m_curseg;

    const unsigned char* rev(const unsigned char* p, unsigned char* buf,
                             int size)
    {
        for (int i = 0; i < size; i++)
            buf[i] = p[size - i - 1];
        return buf;
    }

    template <typename T> bool setKeyValueByPerPos(unsigned short per)
    {
        m_lastCompSize = sizeof(T);
        double k = (*((T*)m_last) - *((T*)m_first)) / (double)10000;
        T v = (T)((per * k) + *((T*)m_first) + m_period);

        memcpy(m_current, (unsigned char*)&v, m_lastCompSize);
        return ((*((T*)m_last) - *((T*)m_first)) == 1);
    }

    template <class T> bool setKeyValueByPerPosRev(unsigned short per)
    {
        uchar buf1[10];
        uchar buf2[10];
        m_lastCompSize = sizeof(T);
        const unsigned char* fr = rev(m_first, buf1, m_lastCompSize);
        const unsigned char* lr = rev(m_last, buf2, m_lastCompSize);
        double k = (*((T*)lr) - *((T*)fr)) / (double)10000;
        T v = (T)((per * k) + *((T*)fr) + m_period);

        memcpy(m_current, rev((const unsigned char*)&v, buf1, m_lastCompSize),
               m_lastCompSize);

        return ((*((T*)fr) - *((T*)lr)) == 1);
    }

    void movePosition(int size)
    {
        m_first += size;
        m_last += size;
        m_current += size;
        m_lastCompPos += size;
    }

    /** if all segment is same return false
     *
     */
    bool seekDifferentKey()
    {
        int pos = 0;
        for (int i = 0; i < (int)m_key.user_defined_key_parts; i++)
        {
            m_curseg = &m_key.key_part[i];
            if (memcmp(m_first + pos, m_last + pos, m_curseg->store_length))
                break;
            pos += m_curseg->store_length;
        }
        if (pos)
        {
            memcpy(m_current, m_first,
                   pos); // A key value is the same value so far.
            movePosition(pos);
        }
        return (pos != (int)m_key.key_length);
    }

public:
    percentageKey(const KEY& key, uchar* first, uchar* last, uchar* current)
        : m_first(first), m_last(last), m_current(current), m_key(key),
          m_curseg(&key.key_part[0])
    {
    }

    void reset(uchar* first, uchar* last, uchar* current)
    {
        m_first = first;
        m_last = last;
        m_current = current;
        m_curseg = &m_key.key_part[0];
        m_lastCompPos = 0;
    }

    /**
     *  @return 0 succes, 1 allsegmnet are same, 2 need segment copy
     */
    int setKeyValueByPer(unsigned short per, bool forwoard)
    {
        m_period = (forwoard) ? 0.5f : -0.5f;
        if (!seekDifferentKey())
            return KEY_ALL_SGMENTS_SAME;

        int len = m_curseg->store_length;
        if (m_curseg->null_bit)
        {
            --len;
            movePosition(1);
        }

        bool needCopy = false;
        switch (m_curseg->field->key_type())
        {
        case HA_KEYTYPE_FLOAT:
        case HA_KEYTYPE_DOUBLE:
        {
            if (len == 4)
                needCopy = setKeyValueByPerPos<float>(per);
            else if (len == 8)
                needCopy = setKeyValueByPerPos<double>(per);
            break;
        }
        case HA_KEYTYPE_USHORT_INT:
        case HA_KEYTYPE_ULONG_INT:
        case HA_KEYTYPE_ULONGLONG:
        {
            switch (len)
            {
            case 1:
                needCopy = setKeyValueByPerPos<unsigned char>(per);
                break;
            case 2:
                needCopy = setKeyValueByPerPos<unsigned short>(per);
                break;
            case 4:
                needCopy = setKeyValueByPerPos<unsigned int>(per);
                break;
            case 8:
                needCopy = setKeyValueByPerPos<unsigned __int64>(per);
                break;
            }
            break;
        }
        case HA_KEYTYPE_INT8:
        case HA_KEYTYPE_SHORT_INT:
        case HA_KEYTYPE_LONG_INT:
        case HA_KEYTYPE_LONGLONG:
        {
            switch (len)
            {
            case 1:
                needCopy = setKeyValueByPerPos<char>(per);
                break;
            case 2:
                needCopy = setKeyValueByPerPos<short>(per);
                break;
            case 4:
                needCopy = setKeyValueByPerPos<int>(per);
                break;
            case 8:
                needCopy = setKeyValueByPerPos<__int64>(per);
                break;
            }
            break;
        }
        case HA_KEYTYPE_VARTEXT1:
        case HA_KEYTYPE_VARBINARY1:
        case HA_KEYTYPE_VARTEXT2:
        case HA_KEYTYPE_VARBINARY2:

            len = m_curseg->length;
            m_varlenPtr = (unsigned short*)m_current;
            *m_varlenPtr = len;
            movePosition(2);
        case HA_KEYTYPE_TEXT:
        case HA_KEYTYPE_BIT:
        case HA_KEYTYPE_BINARY:
        {

            // It copies until every 1 byte of values differ,
            // and the following 2 bytes are made into the order of reverse,
            // and it processes as unsigned short.
            for (int i = 0; i < len; i++)
            {
                if (*m_first == *m_last)
                {
                    *m_current = *m_first;
                    movePosition(sizeof(char));
                }
                else
                {
                    int size = (len - i);

                    if (size >= 8)
                        needCopy =
                            setKeyValueByPerPosRev<unsigned __int64>(per);
                    else if (size >= 4)
                        needCopy = setKeyValueByPerPosRev<unsigned int>(per);
                    else if (size >= 2)
                        needCopy = setKeyValueByPerPosRev<unsigned short>(per);
                    else
                        needCopy = setKeyValueByPerPos<unsigned char>(per);
                    break;
                }
            }

            break;
        }
        case HA_KEYTYPE_INT24:
        case HA_KEYTYPE_UINT24:
        case HA_KEYTYPE_NUM:
        case HA_KEYTYPE_END:
            // no support
            break;
        }
        if (!forwoard && needCopy)
            return KEY_NEED_SGMENT_COPY;
        return KEY_COPY_SUCCESS;
    }

    void copyFirstDeferentSegment()
    {

        memcpy(m_last, m_first, m_lastCompSize);
        // After area that pad by ff;
        memset(m_last + m_lastCompSize, 0xff,
               m_key.key_length - m_lastCompSize - m_lastCompPos);
    }
};

} // namespace mysql
} // namespace engine
} // namespace db
} // namespace bzs

#endif // BZS_DB_ENGINE_MYSQL_PERCENTAGEKEY_H
